package com.asiainfo.exec.core;

import com.beust.jcommander.JCommander;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.Map;

@Component
public class TaskCommandLineRunner implements CommandLineRunner, BeanFactoryPostProcessor {

    private final static Logger LOGGER = LoggerFactory.getLogger(TaskCommandLineRunner.class);

    private Map<String, TaskRunner> taskRunners;

    @Override
    public void run(String... args) throws Exception {
        String[] springArgs = SpringTaskApplication.getSpringArgs();
        LOGGER.info("Spring args {}", Arrays.toString(springArgs));

        String[] taskArgs = SpringTaskApplication.getTaskArgs();
        LOGGER.info("Task args {}", Arrays.toString(taskArgs));

        doTask(taskArgs);
    }

    private void doTask(String[] taskArgs) {
        String taskName = SpringTaskApplication.getTaskName();
        TaskRunner task = taskRunners.get(taskName);
        if (task == null) {
            LOGGER.warn("No task !");
            printUsage();
            return;
        }
        CmdArgs args;
        try {
            Class<?> clz = getArgsClassType(task);
            args = (CmdArgs) clz.newInstance();
            args.setArgs(taskArgs);

            JCommander.newBuilder()
                    .addObject(args)
                    .build()
                    .parse(taskArgs);
        } catch (Throwable e) {
            LOGGER.error("{} parse args failed", task.getClass().getName(), e);
            printUsage(taskName);
            return;
        }
        try {
            task.run(args);
        } catch (Throwable e) {
            LOGGER.error("{} run failed", task.getClass().getName(), e);
        }
    }

    private void printUsage() {
        for (String taskName : taskRunners.keySet()) {
            printUsage(taskName);
        }
    }

    private void printUsage(String taskName) {
        try {
            System.out.printf(">=========== EXECUTE [ %s ] TASK ===========<\n", taskName);
            TaskRunner task = this.taskRunners.get(taskName);
            Class<?> clz = getArgsClassType(task);
            Object args = clz.newInstance();
            JCommander jc = JCommander
                    .newBuilder()
                    .addObject(args)
                    .build();
            jc.usage();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Class<?> getArgsClassType(TaskRunner task) {
        Class<?> clz = AopUtils.getTargetClass(task);
        ParameterizedType parameterizedType = (ParameterizedType) clz.getGenericInterfaces()[0];
        Class<?> argsType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
        return argsType;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        this.taskRunners = beanFactory.getBeansOfType(TaskRunner.class);
    }

}
